const fs = require("fs");
const path = require("path");
const OSS = require("ali-oss");
const { ipcMain } = require("electron");

const getUsers = (users) => {
  const us = {};
  if (users) {
    users.forEach((u) => {
      us[u.uid] = u.name;
    });
  }
  return us;
};

const toNumber = (str) => {
  return isNaN(str) ? 0 : Number(str);
};

/**
 * 日志字段参见： https://help.aliyun.com/document_detail/31868.html
 * res: [Time, Request-UR, Referer, User-Agent, HostName, Request ID, LoggingFlag, Requester Aliyun ID, Operation, Bucket, Key, Error Code, UserID, Sync Reques, StorageClass,TargetStorageClass, null]
 * other: [Remote IP, Reserved, Reserved, HTTP Status, SentBytes, RequestTime (ms), ObjectSize, Server Cost Time (ms), Request Length, Delta DataSize]
 */
const handleUser = (logOne, data, users) => {
  const res = logOne
    .trim()
    .match(/".*?"|\[.*?\]/g)
    .map((v) => v.replace(/"/g, ""));
  const uid = res[7];
  if (!(uid in data)) {
    data[uid] = {
      // 配置的用户备注
      name: users[uid] || (uid == "-" ? "匿名" : uid),
      // 返回错误的请求
      errors: {},
      errorCount: 0,
      // 增加的大小
      increasedSize: 0,
      // 减少的大小
      reducedSize: 0,
      // 外网流量
      outTraffic: 0,
      // 操作
      operations: {},
    };
  }
  const others = logOne
    .trim()
    .replace(/".*?"|\[.*?\]/g, " ")
    .split(/\s+/);
  const status = toNumber(others[3]);
  const operation = res[8];
  if (status == 200) {
    const dataSize = toNumber(others[9]);
    if (dataSize > 0) {
      data[uid].increasedSize += dataSize;
    } else {
      data[uid].reducedSize += dataSize;
    }
    const host = res[4];
    if (host.indexOf("-internal.aliyuncs.com") < 0) {
      const traffic = toNumber(others[8]);
      data[uid].outTraffic += traffic;
    }
    if (!(operation in data[uid].operations)) {
      data[uid].operations[operation] = 0;
    }
    data[uid].operations[operation] += 1;
  } else {
    data[uid].errorCount += 1;
    const errCode = res[11];
    if (!(operation in data[uid].errors)) {
      data[uid].errors[operation] = {};
    }
    if (!(errCode in data[uid].errors[operation])) {
      data[uid].errors[operation][errCode] = 0;
    }
    data[uid].errors[operation][errCode] += 1;
  }
  return data;
};

const handleLocalLogs = (
  { local, prefix, startTime, endTime, ...rest },
  users
) => {
  const data = {};
  const files = fs.readdirSync(local.dir);
  if (files.length === 0) {
    return { status: 1, message: "所选日志位置无文件" };
  }
  const fileStartName = prefix + startTime;
  const fileEndName = prefix + endTime;
  let total = 0;
  for (const file of files) {
    if (file >= fileStartName && file <= fileEndName) {
      const logText = fs.readFileSync(path.resolve(local.dir, file), "utf-8");
      const logArr = logText.split("\n");
      logArr.forEach((logOne) => {
        if (logOne.trim()) {
          if (rest.handleWay == "user") {
            handleUser(logOne.trim(), data, users);
          }
        }
      });
      total += 1;
    }
  }
  if (total === 0) {
    return { status: 1, message: "没有对应前缀以及日期的日志文件" };
  }
  return {
    status: 0,
    message: "共处理 " + total + " 个文件",
    data,
  };
};

const handleClientLogs = async ({ connection, ...rest }, users) => {
  const data = {};
  const { endpoint, keyid, keysecret, cname, bucket, prefix } = connection;
  const client = new OSS({
    endpoint,
    accessKeyId: keyid,
    accessKeySecret: keysecret,
    bucket,
    cname,
  });
  let successTotal = 0,
    filedTotal = 0;
  let nextMarker = prefix + rest.prefix + rest.startTime + "-001";
  while (nextMarker && nextMarker <= prefix + rest.prefix + rest.endTime) {
    const result = await client.list({
      prefix: prefix + rest.prefix,
      marker: nextMarker,
    });
    if (result.res.statusCode === 200) {
      for (const file of result.objects) {
        if (file.name <= prefix + rest.prefix + rest.endTime) {
          const res = await client.get(file.name);
          if (res.res.statusCode === 200) {
            successTotal += 1;
            const buffer = Buffer.from(res.content);
            const logText = buffer.toString();
            const logArr = logText.split("\n");
            logArr.forEach((logOne) => {
              if (logOne.trim()) {
                if (rest.handleWay == "user") {
                  handleUser(logOne.trim(), data, users);
                }
              }
            });
          } else {
            filedTotal += 1;
          }
        }
      }
      nextMarker = result.nextMarker;
    } else {
      nextMarker = null;
    }
  }
  if (successTotal === 0) {
    return { status: 1, message: "没有对应前缀以及日期的日志文件" };
  }
  return {
    status: 0,
    message:
      "共成功处理 " +
      successTotal +
      " 个文件" +
      (filedTotal ? " 处理失败 " + filedTotal + " 个文件" : ""),
    data,
  };
};

const handleStatistics = async (params) => {
  try {
    const users = getUsers(params.users);
    if (params.readWay === "local") {
      return handleLocalLogs(params, users);
    } else if (params.readWay === "client") {
      // 在线查看日志文件
      return await handleClientLogs(params, users);
    }

    return { status: 2, message: "无效的读取方式" };
  } catch (error) {
    return { status: 2, message: JSON.stringify(error) };
  }
};

ipcMain.on("handleStatistics", async (event, params = {}) => {
  params.users = global.config.users;
  params.prefix = global.config.prefix;
  if (params.readWay === "local") {
    params.local = global.config.local;
  } else if (params.readWay === "client") {
    params.connection = global.config.connection;
  }
  event.reply("showResultData", await handleStatistics(params));
});
